#pragma once
/*
模块说明：提供服务注册接口给提供服务的主机端使用
服务主机通过该接口给服务中心发送注册服务请求信息
*/
#include "requestor.hpp"
#include <unordered_set>
class RegistProvider
{
public:
    using RPPtr = std::shared_ptr<RegistProvider>;
    RegistProvider(const std::shared_ptr<Client::Resquestor> &ptr) : _requestor(ptr)
    {
    }

    // 注册指定的服务--提供注册的方法和主机信息
    bool RegisterMethod(const Abstract::BaseConnection::ConPtr &Con, const std::string &method, const Message::Address &Host)
    {
        auto Ser_Msg = Message::BuildMessage::build<Message::ServerRequest>();
        Ser_Msg->SetMtype(Rs_RqType::Service_Req);
        Ser_Msg->SetUuid(UUID::uuid());
        Ser_Msg->SetMethod(method);
        Ser_Msg->SetHost(Host);
        Ser_Msg->SetSerOper(ServiceOptype::SERVICE_Register);

        // 同步等待消息回应
        Abstract::BaseMessage::MesaPtr Mptr;
        bool ret = _requestor->Send(Con, Ser_Msg, Mptr);
        if (!ret)
        {
            ELOG("%s务请求发送失败", method.c_str());
            return false;
        }
        if (Mptr.get() == nullptr)
        {
            ELOG("Server注册服务%s务请求的回应接收失败", method.c_str());
            return false;
        }
        auto SerPtr = std::dynamic_pointer_cast<Message::ServerReponceJson>(Mptr);
        if (SerPtr.get() == nullptr)
        {
            ELOG("基类Message向派生类转化失败");
            return false;
        }
        if (SerPtr->Getcode() != ResCode::ResCode_Ok)
        {
            ELOG("服务注册失败，原因:%s", GetResCodeStr(SerPtr->Getcode()).c_str());
            return false;
        }
        return true;
    }

private:
    // 负责服务注册的请求发送和对应的回应处理
    std::shared_ptr<Client::Resquestor> _requestor;
};
// 再提供一个Discover对象存储客户端在进行服务发现时服务中心发送过来的主机信息
class MethodOfHost
{
public:
    using MthodPtr = std::shared_ptr<MethodOfHost>;
    MethodOfHost(const std::vector<Message::Address> &V)
        : _Hosts(V.begin(), V.end()), NextHost(0)
    {
    }
    MethodOfHost() : NextHost(0)
    {
    }
    // 客户端收到服务上线请求时把服务端发送过来的提供服务的主机信息存储起来
    void Appendhost(const Message::Address &host)
    {
        std::unique_lock<std::mutex> Lock(_mutex);
        _Hosts.push_back(host);
    }
    // 返回当前可以提供服务的主机信息，采用RR轮转策略记录下一个要访问的主机在容器中的位置
    // 能够提供服务的主机挨个请求依次保证负载均衡·
    Message::Address ChooseHost()
    {
        std::unique_lock<std::mutex> Lock(_mutex);
        size_t pos = (NextHost++) % _Hosts.size();
        NextHost = NextHost % _Hosts.size();
        return _Hosts[pos];
    }
    // 客户端收到服务下线请求时把服务端发送过来的提供服务的主机信息存删除
    void Remove(const Message::Address &host)
    {
        std::unique_lock<std::mutex> Lock(_mutex);
        auto it = _Hosts.begin();
        for (auto iit = it; iit != _Hosts.end(); iit++) // 如果该服务记录了对应 的服务主机信息那就删除
        {
            if (*iit == host)
            {
                _Hosts.erase(iit);
                break;
            }
        }
    }
    // 判断当前该服务有没有主机可以提供
    bool empty()
    {
        return _Hosts.empty();
    }

private:
    size_t NextHost;                      // 当前客户端要请求的提供Rpc服务的主机在存储容器中的位置
    std::mutex _mutex;                    // 加锁报=保护可能客户端有多个发送请求的线程会从该对象中获取主机信息
    std::vector<Message::Address> _Hosts; // 使用哈希表存储主机信息方便查找
};

// 该模块负责组织服务发现请求并且发送 和 处理服务发现请求的回应
// 保存有哪些主机提供哪些服务
class Discover
{
public:
    using DPtr = std::shared_ptr< Discover>;
    using DelHostCallback=std::function<void(const Message::Address &Host)>; 
    Discover(const Client::Resquestor::RPtr& re,const  DelHostCallback& Cb) : _requestor(re),_CBack(Cb)
    {
    }
    // 提供 接口给客户端获取当前服务由哪个主机来提供,如果当前服务没有主机提供则组织一条服务发现请求给服务中心
    // 查询有哪些主机提供该服务，如果也没有那就不发送Rpc请求了
    bool GetDiscoverService(const Abstract::BaseConnection::ConPtr &Con, const std::string &method, Message::Address &Host)
    {
        {
            std::unique_lock<std::mutex> Lock(_mutex);
            //ELOG("进入到发送服务查询模块");
            if (_Mthod_Hosts.count(method))
            {
                if (!_Mthod_Hosts[method]->empty())
                {
                    Host = _Mthod_Hosts[method]->ChooseHost(); // 当前有主机提供服务
                    //ELOG("当前有主机提供服务");
                    return true;
                }
            }
        }
        // 当前没有主机们可以提供该服务，组织一次服务发现请求向服务中心询问该服务有哪些主机提供
        auto Ser_Msg = Message::BuildMessage::build<Message::ServerRequest>();
        Ser_Msg->SetMtype(Rs_RqType::Service_Req);
        Ser_Msg->SetUuid(UUID::uuid());
        Ser_Msg->SetMethod(method);
        Ser_Msg->SetSerOper(ServiceOptype::SERVICE_Discover);
        // 同步等待消息回应
        Abstract::BaseMessage::MesaPtr Mptr;
        bool ret = _requestor->Send(Con, Ser_Msg, Mptr);
        if (!ret)
        {
            //ELOG("%s服务发现请求发送失败", method.c_str());
            return false;
        }
        if (Mptr.get() == nullptr)
        {
            //ELOG("Server服务发现 :%s服务的回应接收失败", method.c_str());
            return false;
        }
        auto SerPtr = std::dynamic_pointer_cast<Message::ServerReponceJson>(Mptr);
        if (SerPtr.get() == nullptr)
        {
            //ELOG("基类Message向派生类转化失败");
            return false;
        }
        if (SerPtr->Getcode() != ResCode::ResCode_Ok)
        {
            ELOG("服务发现请求失败，原因:%s", GetResCodeStr(SerPtr->Getcode()).c_str());
            return false;
        }
        // 走到这里说明是没有哪能够提供该服务的主机的，然后查询服务中心之后获取到的主机信息
        std::unique_lock<std::mutex> Lock(_mutex);
        std::vector<Message::Address> V;
        SerPtr->GetDisResult(V);
        if (_Mthod_Hosts.count(method)) // 如果当前提供的服务为空
        {
            for (auto &Host1 : V)
            {
                _Mthod_Hosts[method]->Appendhost(Host1);
            }
            Host = _Mthod_Hosts[method]->ChooseHost();
            return true;
        }
        // 当前没有该服务名称和对应提供该服务的主机的记录信息
        auto Meptr = std::make_shared<MethodOfHost>(V);
        _Mthod_Hosts.insert({method, Meptr});
        Host = Meptr->ChooseHost();
        return true;
    }
    // 处理服务中心发过来的服务上线和下线的请求，将来注册到客户端的Dispather模块来回调处理这些请求
    void DealServerRequest(const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &ReqPtr)
    {
        auto SerPtr = std::dynamic_pointer_cast<Message::ServerRequest>(ReqPtr);
        if (SerPtr->GetSerOper() == ServiceOptype::SERVICE_Online)
        {
            // 1 如果是服务上线通知
            // ，虽然说只有请求过该服务的客户端才会收到该请求
            // 但是为了健壮性，防止空的智能指针访问导致程序崩溃还是加上判断
            if (_Mthod_Hosts.count(SerPtr->method())) // 如果该服务之前请求过
            {
                _Mthod_Hosts[SerPtr->method()]->Appendhost(SerPtr->GetHost());
            }
            else
            {
                // 如果该服务之前没有请求过,构建一个服务名称 和提供该服务的主机的映射关系
                auto Meptr = std::make_shared<MethodOfHost>();
                Meptr->Appendhost(SerPtr->GetHost());
                _Mthod_Hosts.insert({SerPtr->method(), Meptr});
            }
        }
        else if (SerPtr->GetSerOper() == ServiceOptype::SERVICE_Offline)
        {
            // 2 如果是服务下线通知
            if (_Mthod_Hosts.count(SerPtr->method())) // 如果该服务之前请求过
            {
                _Mthod_Hosts[SerPtr->method()]->Remove(SerPtr->GetHost());
                _CBack(SerPtr->GetHost());//回调到Rpc客户端模块删除对应连接池中的链接对象
            }
            else
            {
                // 如果该服务之前没有请求过,那就什么都不要做
                return;
            }
        }
        else
        {
            // 其它服务请求不处理
            ELOG("接收到了未知的来自服务中心的服务请求");
        }
    }

private:
    std::mutex _mutex;
    DelHostCallback _CBack;//删除Rpc客户端中的主机信息回调函数
    //如果该所能够提供该服务的主机一个都没有了也不用删除服务名称和MthodPtr的映射关系
    //因为下一次该服务可能还会上线到时候直接找到对应的MthodPtr对象直接添加主机信息即可
    //不用再去创建MthodPtr和服务名称的映射关系了是、
    std::unordered_map<std::string, MethodOfHost::MthodPtr> _Mthod_Hosts; // 保存服务提供者的信息
    Client::Resquestor::RPtr _requestor;
};